/*
* Sun Public License Notice
*
* The contents of this file are subject to the Sun Public License
* Version 1.0 (the "License"). You may not use this file except in
* compliance with the License. A copy of the License is available at
* http://www.sun.com/
*
* The Original Code is Forte for Java, Community Edition. The Initial
* Developer of the Original Code is Sun Microsystems, Inc. Portions
* Copyright 1997-2000 Sun Microsystems, Inc. All Rights Reserved.
*/
package org.openide.compiler;
import java.util.*;
import java.lang.ref.*;
import org.openide.loaders.*;
/** Implementable object representing one task of compilation.
* <p>A module's implementation should typically be instantiated within
* an implementation of {@link org.openide.cookies.CompilerCookie#addToJob},
* or (preferably) {@link Compiler.Manager#prepareJob}.
* It need not do much beyond keep track of the particular file (data object, ...)
* it is associated with and make sure the associated {@link CompilerGroup} implementation
* knows about it (via {@link CompilerGroup#add}).
* <P>
* <B>Important:</B> Each compiler should implement <code>equals (Object)</code> and
* <code>hashCode ()</code> methods
* to work correctly. That means to check whether the file (data object, ...) that
* should be compiled is the same or not. Two Compilers should be equal even
* if they have different set of dependencies, they do not matter. <code>hashCode ()</code>
* must be consistent with <code>equals (Object)</code>.
*
* @author Ales Novak, Jaroslav Tulach, Petr Hamernik
*/
public abstract class Compiler extends Object implements Compilable {
/** Zero level of compilation--leaves should compile themself, but folders should do nothing. */
public static final Depth DEPTH_ZERO = new Depth();
/** Penultimate level of compilation--folder should compile just directly contained files, but not recursively. */
public static final Depth DEPTH_ONE = new Depth();
/** Infinite level of compilation--compile all folders recursively. */
public static final Depth DEPTH_INFINITE = new Depth();
/** set of compilable objects that have to be processed before this
* compiler can be executed.
*/
private java.util.Collection dependsOn;
/** Default constructor.
*/
public Compiler () {
}
/** Create a new compiler belonging to a specified compiler job.
* This is the most commonly used constructor.
*
* @param job the job the compiler belongs to
* @deprecated use job.add (new Compiler ());
*/
public Compiler (CompilerJob job) {
job.add (this);
registerInJob (job);
}
/** Create a new compiler that depends on another.
* The new compiler is put into the same job as the other compiler.
* @param c the compiler to depend on
* @deprecated use new Compiler ().dependsOn (c);
*/
public Compiler (Compiler c) {
dependsOn (c);
registerInJob (c);
}
/** Create a new compiler that depends on several others.
* Placed in the same job as the others.
* @param dep compilers to depend on
* @deprecated use new Compiler ().dependsOn (Arrays.asList (dep));
*/
public Compiler (Compiler[] dep) {
dependsOn (Arrays.asList (dep));
registerInJob (dep[0]);
}
/** Check whether the compiler is up to date.
* This method is called from {@link CompilerJob#isUpToDate}
* to skip a compilation when it is unnecessary.
*
* @return <code>true</code> if up-to-date; <code>false</code> if compilation is needed
*/
protected abstract boolean isUpToDate ();
/** Get the associated <code>CompilerGroup</code> container class.
* The compiler and compiler group should typically be implemented in parallel,
* though it is possible to subclass a compiler without subclassing the compiler group.
* The compiler group must be "expecting" a compiler of this class (or a superclass), or else
* it is free to fail in an unspecified way.
* All instances of the same compiler class must return the same result from this method.
* @return a class assignable to {@link CompilerGroup}
*/
public abstract Class compilerGroupClass ();
/** Specify a unique key permitting division of a set of compilers into different compiler groups.
* The compilation engine, in {@link CompilationEngine#createCompilerGroups}, will always separate
* compilers within a given job into levels based on their stated dependencies; however, it will
* also split compilers into different groups within each level, based on this key. All compilers
* with the same key which can fit into the same level will be placed into a single group.
* No group will contain compilers with different values of the key.
*
* <p>This key should only be used for grouping; it is not available to the compiler group, as
* its content may be idiosyncratic. (Compiler groups requiring specific pieces of information
* from the compilers added to them must extract this information according to accessible methods
* or fields of the compilers, as they are {@link CompilerGroup#add added}.) The key must obey
* the general Java language contract for object comparison, according to {@link Object#equals}
* and {@link Object#hashCode}.
*
* <p>By default, only the {@link #compilerGroupClass} is used. It is required that each compiler
* class, if it needs to override this method, first call the super method and add distinctions to
* this result. I.e. a subclass may never produce a key less discriminating than the superclass.
* The suggested implementation is to call the super method, and then return a {@link java.util.List}
* containing both the super's result, as well as any information added
* by this subclass.
*
* <p>Additionally, in the unusual but possible case that a compiler is subclassing another
* compiler, while still using the same compiler group class as its superclass, the subclass compiler <em>must</em>
* include in the key (e.g. an additional vector element) the class object for the subclass (determined via a static class
* constant, <em>not</em> {@link Object#getClass}!)--this will ensure that keys from unrelated subclasses of the
* same compiler superclass will not randomly conflict.
*
* @return the grouping key object for this compiler
*/
public Object compilerGroupKey () {
return compilerGroupClass ();
}
/** Add dependency on a compilable object. That means that this compiler
* can be started only after all compilable objects has been finished.
*
* @param compilable any compilable object (Compiler, CompilerJob, etc.)
*/
public final void dependsOn (final Compilable compilable) {
dependsOn (Collections.singleton(compilable));
}
/** Add dependency on a compilable object. That means that this compiler
* can be started only after all compilable objects has been finished.
*
* @param compilables collection of Compilable
*/
public void dependsOn (final Collection compilables) {
MUTEX.readAccess (new Runnable () {
public void run () {
synchronized (Compiler.this) {
if (dependsOn == null) {
dependsOn = new IdSet ();
}
dependsOn.addAll (compilables);
}
}
});
}
/** Returns itself.
*
* @return java.util.Collections.singleton (this);
*/
public final java.util.Collection compilers() {
return java.util.Collections.singleton (this);
}
/** A collection of other Compilable objects that have to be
* finished before the compilers of this Compilable can be started.
*
* @return collection of Compilable
*/
public final java.util.Collection dependsOn() {
// JST: Maybe we could return immutable collection but who really cares...
return dependsOn == null ? java.util.Collections.EMPTY_LIST : dependsOn;
}
/** Depth of compilation.
*
* @see Compiler#DEPTH_ZERO
* @see Compiler#DEPTH_ONE
* @see Compiler#DEPTH_INFINITE
*/
public static final class Depth extends Object {
/** Nobody should construct this class. Everybody must use predefined constants */
Depth() {
}
/** Is this the last level (i.e. zero)?
* @return <code>true</code> if so
*/
public boolean isLastDepth () {
return this == DEPTH_ZERO;
}
/** Proceed to the next level.
* @return the next shallower depth
*/
public Depth nextDepth () {
if (this == DEPTH_INFINITE) {
// stay always on infinite depth
return this;
} else {
// aways proceed to first level
return DEPTH_ZERO;
}
}
}
//
// Hack for backward compatibility
//
/** map associating compilers with job */
private static Map jobTable = new WeakHashMap (101);
/** Registers a compiler with a job */
final void registerInJob (CompilerJob job) {
jobTable.put (this, new WeakReference (job));
}
/** Finds a job for a compiler and registers it.
*/
final void registerInJob (Compiler comp) {
Reference ref = (Reference)jobTable.get (comp);
CompilerJob job = ref == null ? null : (CompilerJob)ref.get ();
if (job == null) throw new IllegalStateException ("Argument Compiler is not in any job"); // NOI18N
jobTable.put (this, ref);
job.add (this);
}
}
/*
* Log
* 17 Gandalf 1.16 1/18/00 Jaroslav Tulach External Compiler is
* initialized first and than its dependencies are handled (caused
* problems in the hashCode) method.
* 16 Gandalf 1.15 1/15/00 Petr Jiricka Javadoc updated.
* 15 Gandalf 1.14 1/12/00 Ian Formanek NOI18N
* 14 Gandalf 1.13 12/23/99 Jaroslav Tulach Enhancing compiler API
* to makefile capabilities
* 13 Gandalf 1.12 10/22/99 Ian Formanek NO SEMANTIC CHANGE - Sun
* Microsystems Copyright in File Comment
* 12 Gandalf 1.11 9/10/99 Jaroslav Tulach CompilerJob has method
* mergeInto (CompilerJob)
* 11 Gandalf 1.10 9/10/99 Jesse Glick Small API change:
* ExternalCompiler.compilerType -> Compiler.compilerGroupKey.
* 10 Gandalf 1.9 9/10/99 Jaroslav Tulach Compiles with Jikes.
* 9 Gandalf 1.8 8/9/99 Ian Formanek Generated Serial Version
* UID
* 8 Gandalf 1.7 6/8/99 Ian Formanek ---- Package Change To
* org.openide ----
* 7 Gandalf 1.6 4/16/99 Jesse Glick Compiler.Manager.find()
* now takes class rather than instance of DO.
* 6 Gandalf 1.5 4/1/99 Jaroslav Tulach Not crashes when there
* is no compiler.
* 5 Gandalf 1.4 3/24/99 Jesse Glick [JavaDoc]
* 4 Gandalf 1.3 3/24/99 Jesse Glick [JavaDoc]
* 3 Gandalf 1.2 2/19/99 Jaroslav Tulach More compiler managers.
* 2 Gandalf 1.1 1/6/99 Jaroslav Tulach
* 1 Gandalf 1.0 1/5/99 Ian Formanek
* $
* Beta Change History:
* 0 Tuborg 0.11 --/--/98 Jaroslav Tulach Comments, made more abstract (removed array of FileObjects)
* 0 Tuborg 0.12 --/--/98 Petr Hamernik depth added
* 0 Tuborg 0.13 --/--/98 Petr Hamernik interface changed
*/